﻿using System.IO.Compression;
using System.Reflection;
using D.Util.UtilCache;
using D.Util.UtilConsole;
using D.UtilCore.Attributes;
using D.UtilCore.Dtos;
using D.UtilCore.Middlewares;
using D.UtilCore.Models;
using D.UtilCore.Routes;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using D.Util.UtilInterfaceDoc;
using Microsoft.AspNetCore.Http;
using D.Util.UtilNLog;
using D.Util.UtilIoc;
using D.UtilCore.MediatRUtils;
using D.Util.UtilConfiguration;
using Microsoft.AspNetCore.ResponseCompression;
using FluentValidation.AspNetCore;
using FluentValidation;
using D.UtilCore.JwtUtil;
using System.Net;
using Microsoft.AspNetCore.Hosting;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Mvc;
using D.Util.UtilMiniProfiler;
using D.UtilCore.WChartUtils;
using D.Util.UtilHangfireJob;
using Hangfire;
using System.Text.Json;
using D.UtilFreesqlOrm;

namespace D.UtilCore
{
    public static class BaseApi
    {
        /// <summary>
        /// 静态实例
        /// </summary>
        public static IServiceProvider instance { get; set; }

        /// <summary>
        /// 全局配置文件
        /// </summary>
        public static AppConfig appConfig { get; set; }

        /// <summary>
        /// 获取全部注释Xml文档
        /// </summary>
        public static List<string> XmlCommentsFilePath
        {
            get
            {
                var basePath = AppContext.BaseDirectory;
                DirectoryInfo d = new DirectoryInfo(basePath);
                FileInfo[] files = d.GetFiles("*.xml");
                var xmls = files.Select(a => Path.Combine(basePath, a.FullName)).ToList();
                return xmls;
            }
        }

        /// <summary>
        /// 全部控制器的权限列表
        /// </summary>
        public static List<PermissionDto> permissions = new List<PermissionDto>();

        /// <summary>
        /// 获取全部控制器中标识的模块权限集合
        /// </summary>
        private static void GetAppPermission()
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assembly in assemblies)
            {
                var types = assembly.GetTypes()
                    .Where(type => !type.IsAbstract)
                    .Where(type => typeof(Microsoft.AspNetCore.Mvc.ControllerBase).IsAssignableFrom(type)
                    || typeof(Microsoft.AspNetCore.Mvc.Controller).IsAssignableFrom(type)).ToList();

                types.ForEach(type =>
                {
                    var permissionGroup = type.GetCustomAttribute<PermissionGroupAttribute>();
                    foreach (var methodInfo in type.GetMethods())
                    {
                        var permissionAttr = methodInfo.GetCustomAttributes<PermissionAttribute>().FirstOrDefault();

                        if (permissionAttr is not null)
                        {
                            var permission = new PermissionDto
                            {
                                Group = permissionAttr.Group,
                                Name = permissionAttr.Name,
                                Description = permissionAttr.Description,
                                Sign = $"{type.Name}.{methodInfo.Name}".ToUpper().Replace("CONTROLLER", "")
                            };
                            if (string.IsNullOrEmpty(permission.Group))
                            {
                                if (permissionGroup is null)
                                {
                                    permissionGroup = new PermissionGroupAttribute(type.Name.Replace("Controller", ""));
                                };
                                permission.Group = permissionGroup.Name;
                            }
                            permissions.Add(permission);
                        }
                    }
                });
            }

            // 缓存
            var cache = instance.GetService<ICacheService>();
            if (cache != null && permissions.Count > 0)
            {
                cache.SetAsync("permissions", permissions);
            }
        }

        public static bool AddService(WebApplicationBuilder builder, IConfiguration configuration, IHostEnvironment environment)
        {
            var services = builder.Services;
            //builder.WebHost.UseUrls("http://localhost:8787");
            services.AddHealthChecks();

            ConsoleUtil.Info("当前环境：" + environment.EnvironmentName);

            // 添加自动加载配置文件
            configuration.AddConfigureFiles(environment);

            // 获取全局配置文件内容
            appConfig = configuration.Get<AppConfig>();
            if (appConfig == null || appConfig.DbConfig == null)
            {
                ConsoleUtil.Err("加载配置文件失败");
                return false;
            }

            // 网站应用进行了负载均衡或者挂了CDN，获取IP地址
            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardLimit = null;// 限制所处理的标头中的条目数
                options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; // X-Forwarded-For：保存代理链中关于发起请求的客户端和后续代理的信息。X-Forwarded-Proto：原方案的值 (HTTP/HTTPS)    
                options.KnownNetworks.Clear(); // 从中接受转接头的已知网络的地址范围。 使用无类别域际路由选择 (CIDR) 表示法提供 IP 范围。使用CDN时应清空
                options.KnownProxies.Clear(); // 从中接受转接头的已知代理的地址。 使用 KnownProxies 指定精确的 IP 地址匹配。使用CDN时应清空
            });

            #region 后台返回给前端的数据进行数据压缩
            services.Configure<BrotliCompressionProviderOptions>(options =>
            {
                options.Level = CompressionLevel.Optimal;
            })
            .Configure<GzipCompressionProviderOptions>(options =>
            {
                options.Level = CompressionLevel.Optimal;
            })
            .AddResponseCompression(options =>
            {
                options.EnableForHttps = true;
                options.Providers.Add<BrotliCompressionProvider>();
                options.Providers.Add<GzipCompressionProvider>();
                options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
                {
                "text/html; charset=utf-8",
                "application/xhtml+xml",
                "application/atom+xml",
                "image/svg+xml",
                "application/javascript",
                "application/json",
                "application/xml",
                "text/css",
                "text/html",
                "text/json",
                "text/plain",
                "text/xml"
                });
            });
            #endregion

            //指定程序集
            services.AddMediaRs();

            // 自动化注入仓储和服务
            services.AddAutoScanInjection();

            // 上下文
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            // NLog 日志
            services.AddNLogUtil();

            // 缓存
            services.AddMemory();

            // Cors 跨域
            services.AddCors(o =>
            {//CORS Cross-Origin-Resource-Sharing 跨域:跨源资源共享（同源策略） 

                //if (urls.Length > 0)
                //{
                //    o.AddPolicy(policyName, policy =>
                //    {
                //        policy.WithOrigins(urls.Split(','))
                //              .AllowAnyHeader()
                //              .AllowAnyMethod();
                //    });
                //}
                //else
                //{
                ConsoleUtil.Info("允许任意跨域请求：Limit");
                //允许任意跨域请求
                o.AddPolicy("Limit", policy =>
                {
                    policy.SetIsOriginAllowed((host) => true)
                            .AllowAnyOrigin()
                            .AllowAnyHeader()
                            .AllowAnyMethod();
                });

                //}
            });

            // Swagger UI
            if (environment.IsDevelopment() && appConfig.EnableSwagger)
            {
                //获取应用程序所在目录（绝对，不受工作目录影响，建议采用此方法获取路径）
                var basePath = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
                if (basePath != null)
                {
                    ConsoleUtil.Waring("放开接口API文档有风险");
                    services.AddApieDoc(basePath, XmlCommentsFilePath);
                }
            }

            // 启动数据库
            //services.AddSqlSugar(appConfig.DbConfig);
            services.AddOrm(appConfig.DbConfig);

            // 将 IHttpClientFactory 注入到服务容器中
            services.AddHttpClient();

            // 添加微信
            if (appConfig.WxConfig.WxEnable.Value)
            {
                services.AddWeChartUtil();
                services.AddWeChartEnterpriseUtil();
            }
            // 路由以小写形式展示
            services.AddRouting(options =>
            {
                options.LowercaseUrls = true;
            });

            // 定时任务
            if (appConfig.ScheduleTask.ScheduleTaskEnable.Value)
            {
                services.AddHangfire();
            }
            // 控制器
            services.AddControllers(options =>
            {
                // 增加路由统一前缀
                if (!string.IsNullOrWhiteSpace(appConfig.SiteConfig.RoutePrefix))
                {
                    //参考：https://gist.github.com/itavero/2b40dfb476bebff756da35b5c7ff7384
                    //https://www.strathweb.com/2016/06/global-route-prefix-with-asp-net-core-mvc-revisited/
                    //https://benjii.me/2016/08/global-routes-for-asp-net-core-mvc/
                    options.Conventions.Insert(0, new GlobalRoutePrefixConvention(appConfig.SiteConfig.RoutePrefix));
                }

                //添加 工作单元过滤器,使用：调用UnitOfWork的接口上添加特性[UnitOfWork],sqlsugar使用
                options.Filters.Add<UnitOfWorkFilter>();

                // 接口性能监控过滤器
                //options.Filters.Add(typeof(ExtBenchmarkActionUtilFilter));
            })
            .AddJsonOptions(options =>
            {
                // 序列化时key为驼峰样式，首字母小写
                options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            })
            .AddNewtonsoftJson(options =>
            {// 注册 Json 序列化配置 安装Microsoft.AspNetCore.Mvc.NewtonsoftJson

                // 统一设置JSON内实体的格式（默认JSON里的首字母为小写，这里改为同后端Mode一致）   
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

                options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
                // 统一设置API的日期格式
                //options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                // 指定如何解决循环引用：
                //1、Ignore将忽略循环引用
                //2、Serialize将序列化循环引用
                //3、Error将抛出异常
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            })
            .ConfigureApiBehaviorOptions(e =>
            {
                // 关闭默认模型验证
                // e.SuppressModelStateInvalidFilter = true;
                e.InvalidModelStateResponseFactory = actionContext =>
                {
                    //获取验证失败的模型字段 
                    var errors = actionContext.ModelState
                        .Where(e1 => e1.Value.Errors.Count > 0)
                        .Select(e1 => e1.Value.Errors.First().ErrorMessage)
                        .ToList();

                    return new JsonResult(new ApiResult<int>()
                    {
                        Code = StatusCodeEnum.Params,
                        Msg = string.Join("|", errors)
                    });
                };
            }); ;

            #region Ids4 

            //services.AddIdentityServer(o =>
            //{
            //    //o.IssuerUri = ServerUrl;
            //    //o.PublicOrigin = ServerUrl;
            //    //o.Discovery.ShowApiScopes = true;
            //    //o.Discovery.ShowClaims = true;
            //    o.Events.RaiseErrorEvents = true;
            //    o.Events.RaiseFailureEvents = true;
            //    o.Events.RaiseInformationEvents = true;
            //    o.Events.RaiseSuccessEvents = true;
            //})
            ////创建一个用于对token签名的临时密钥材料xx.rsa(但是在生产环境中应该使用可持久的密钥材料):
            ////.AddDeveloperSigningCredential()
            ////添加来自数据库（客户机、资源、CORS）的配置数据
            //.AddDbStore(new DbStoreOptions()
            //{
            //    ConnectionString = appConfig.DbConfig.ConnectionString,
            //    DbType = SqlSugarOrm.GetDbType(appConfig.DbConfig.DbType),

            //    EnableTokenCleanup = appConfig.IdentityServerStoreOptions.EnableTokenCleanup,
            //    TokenCleanupInterval = appConfig.IdentityServerStoreOptions.TokenCleanupInterval,
            //    TokenCleanupBatchSize = appConfig.IdentityServerStoreOptions.TokenCleanupBatchSize,
            //});

            //services.AddIdentity<SysUserEntity, IdentityRole>(o =>
            //{
            //    //密码不需要数字
            //    o.Password.RequireDigit = false;
            //    //密码不区分小写
            //    o.Password.RequireLowercase = false;
            //    //密码不区分大写
            //    o.Password.RequireUppercase = false;
            //    //密码不需要字母数字
            //    o.Password.RequireNonAlphanumeric = false;
            //    //密码长度至少6位
            //    o.Password.RequiredLength = 6;
            //});

            //.AddInMemoryIdentityResources(Config.GetIdentityResources())
            //.AddInMemoryApiResources(Config.GetApis())
            //.AddInMemoryClients(Config.GetClients(WebHostEnvironment.IsProduction()))
            //.AddProfileService<CustomProfileService>()

            #endregion

            #region 统一Bearer授权认证

            // Jwt身份认证
            services.AddJwtAuthorization("AdminPolicy", "AdminRole", appConfig.JwtConfig.SecurityKey, appConfig.JwtConfig.Issuer, appConfig.JwtConfig.Audience);
            #endregion

            #region 实现速率限制,限制来防止API滥用

            //services.AddRateLimiter(options =>
            //{
            //    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
            //        RateLimitPartition.GetFixedWindowLimiter(
            //            partitionKey: httpContext.User.Identity?.Name ?? httpContext.Request.Headers.Host.ToString(),
            //            factory: partition => new FixedWindowRateLimiterOptions
            //            {
            //                AutoReplenishment = true,
            //                PermitLimit = 1000,
            //                QueueLimit = 0,
            //                Window = TimeSpan.FromSeconds(1)
            //            }));
            //});
            #endregion

            // 注册自动验证，用于前端请求过来的自动验证 引用包FluentValidation.AspNetCore
            //Assembly assembly = Assembly.GetExecutingAssembly();
            services.AddFluentValidationAutoValidation()
                    .AddFluentValidationClientsideAdapters();

#if DEBUG
            // .NET性能分析
            services.AddMiniProfilerStartup();
#endif
            //.AddValidatorsFromAssembly(assembly);



            builder.WebHost.UseKestrel(options =>
            {
                options.ListenAnyIP(appConfig.Port, listenOptions =>
                {
                    //listenOptions.Protocols = HttpProtocols.Http1;
                    //参数为证书文件名称，证书密码
                    //listenOptions.UseHttps("fullchain.pfx");
                    //listenOptions.UseHttps("fullchainapi.pfx");
                });
            });

            if (!builder.Environment.IsDevelopment() && appConfig.SiteConfig.UseHttpClusterHttps)
            {// 加载https
                builder.Services.AddHttpsRedirection(options =>
                {
                    options.RedirectStatusCode = (int)HttpStatusCode.PermanentRedirect;
                    options.HttpsPort = appConfig.Port;
                });
            }

            // 超出会出现错误：Failed to read the request form. Multipart body length limit 134217728 exceeded.
            services.Configure<FormOptions>(o =>
            {
                o.ValueLengthLimit = int.MaxValue;
                // 设置表单的最大值
                o.MultipartBodyLengthLimit = int.MaxValue; // 没有设置，默认是128 MB
                o.MultipartHeadersLengthLimit = int.MaxValue;
            });

            services.Configure<KestrelServerOptions>(o =>
            {
                // 设置请求体的最大值，如果不设置请求体大小默认是 30_000_000 bytes，大约28.6MB
                o.Limits.MaxRequestBodySize = int.MaxValue;// 没有设置，默认是30 MB
            });

            return true;
        }

        public static void UseApp(WebApplication app, IHostEnvironment environment)
        {
            #region 异常

            if (environment.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseExceptionHandler(builder =>
                {
                    builder.Run(async context =>
                    {
                        var exceptionHandlerPathFeature = context.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerPathFeature>();
                        if (exceptionHandlerPathFeature != null)
                        {
                            await context.Response.WriteAsync($"error:{exceptionHandlerPathFeature.Error.Message}");
                        }
                    });
                });
            }
            #endregion

            #region 网站应用进行了负载均衡或者挂了CDN，获取IP地址
            // 转发请求头和证书
            app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All }).UseCertificateForwarding();

            //为了获得真实IP地址
            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            }).UseCertificateForwarding();

            #endregion

            if (appConfig.SiteConfig.UseHttpClusterHttps)
            {
                app.UseHttpsRedirection(); //开启Https重定向
            }

            if (appConfig.SiteConfig.UseResponseCompression)
            {
                app.UseResponseCompression(); // 后台返回给前端的数据进行数据压缩
            }

            //app.UseStaticFiles();//启动www静态目录
            //启动上传文件目录
            //app.UseStaticFiles(new StaticFileOptions
            //{
            //    FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(builder.Configuration.GetValue<string>("UploadConfig:UploadDir")),
            //    RequestPath = "/upload/view"
            //});
            //app.UseCookiePolicy();//Cookie策略
            //app.UseHangfire();// 定时任务

            app.UseMiddleware<ExceptionMiddleware>();// 异常中间件

            // 添加微信
            if (appConfig.WxConfig.WxEnable.Value)
            {
                app.UseWeChartUtil();
                app.UseWeChartEnterpriseUtil();
            }
            app.UseRouting();// 路由
            app.UseCors("Limit");// 跨域
            if (appConfig.ScheduleTask.ScheduleTaskEnable.Value)
            {
                app.UseHangfire();// 定时任务
            }
            app.UseAuthentication();// 用于处理用户身份验证
            app.UseAuthorization();// 授权
            //app.UseIdentityServer();// ids4
            //app.UseRateLimiter();// 速率限制
            app.MapHealthChecks("/health");// 健康检查
            foreach (var url in app.Urls)
            {
                Console.WriteLine($"健康检查访问地址为:{url}/health");
            }
            if (appConfig.EnableSwagger)
            { // Api文档
                //app.UseMySwagger();
                // 或者
                app.UseMyKnife4j();
            }

#if DEBUG
            // .NET性能分析
            app.UseMiniProfilerStartup();
#endif

            #region 配置端点

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();

                endpoints.MapControllerRoute(
                  name: "areas",
                  pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
                );
            });
            #endregion

            #region kill 15

            int requestCount = 0;
            bool isExit = false;
            app.Use(async (httpContext, next) =>
            {
                await Console.Out.WriteLineAsync($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}-收到HTTP请求-{requestCount}");
                if (isExit)
                {
                    await Console.Out.WriteLineAsync($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}-已收到退出程序指令，程序正在退出，拒绝接受新消息");
                    httpContext.Abort();
                }
                else
                {
                    Interlocked.Increment(ref requestCount);
                    try
                    {
                        await next();
                    }
                    finally
                    {
                        Interlocked.Decrement(ref requestCount);
                    }
                }
            });

            // 收到关闭请求
            app.Lifetime.ApplicationStopping.Register(async () =>
            {
                await Console.Out.WriteLineAsync($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}-ApplicationStopping start");
                isExit = true;
                SpinWait.SpinUntil(() => requestCount == 0);

                //Console.Out.WriteLineAsync($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}-等待{20000/1000}s后再关闭");
                //Thread.Sleep(20000); 另外一种方案，延迟多少ms后，自动关闭
                //Console.Out.WriteLineAsync($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}-等待完成，执行关闭");
            });

            // 执行关闭
            app.Lifetime.ApplicationStopped.Register(async () =>
            {
                await Console.Out.WriteLineAsync($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}-ApplicationStopped start");
            });
            #endregion

            instance = app.Services;
            //var hostEnvironment = instance.GetRequiredService<IHostEnvironment>();

            // 加载权限
            GetAppPermission();

            appConfig.SiteConfig.ApplicationName = app.Environment.ApplicationName;

            // 配置文件加入缓存
            var cache = instance.GetService<ICacheService>();
            if (cache != null && appConfig != null)
            {
                cache.SetAsync("APP_CONFIG", appConfig);
            }

            ThreadPool.GetMinThreads(out int work, out int comple);
            ThreadPool.GetAvailableThreads(out int worktemp, out int completemp);
            ConsoleUtil.Info($"[{DateTime.Now:HH:mm:ss} 核心数为：{Environment.ProcessorCount}--默认线程最小为：{work}--Available:{worktemp}");
        }
    }
}